// Copyright (C) 2019-2021
// 511972386@qq.com

#include "../include/someip_client.hpp"


namespace someip_app {

static vsomeip::service_t service_id = 0x1111;
static vsomeip::instance_t service_instance_id = 0x2222;
static vsomeip::method_t service_method_id = 0x3333;


// Get the vSomeIP runtime and
    // create a application via the runtime, we could pass the application name
    // here otherwise the name supplied via the VSOMEIP_APPLICATION_NAME
    // environment variable is used
someip_client::someip_client():
                rtm_(vsomeip::runtime::get()),app_(rtm_->create_application())
{
}

someip_client::~someip_client()
{
}

bool someip_client::init()
{
    // init the application
    if (!app_->init()) {
        std::cerr << "Couldn't initialize application" << std::endl;
        return false;
    }

    // register a state handler to get called back after registration at the
    // runtime was successful
    app_->register_state_handler(
            std::bind(&someip_client::on_state_cbk, this, std::placeholders::_1));

    // register a callback for responses from the service
    app_->register_message_handler(vsomeip::ANY_SERVICE,
            service_instance_id, vsomeip::ANY_METHOD,
            std::bind(&someip_client::on_message_cbk, this, std::placeholders::_1));

    // register a callback which is called as soon as the service is available
    app_->register_availability_handler(service_id, service_instance_id,
            std::bind(&someip_client::on_availability_cbk, this,
                    std::placeholders::_1, std::placeholders::_2,
                    std::placeholders::_3));
    return true;
}


void someip_client::start()
{
    // start the application and wait for the on_event callback to be called
    // this method only returns when app_->stop() is called
    app_->start();
}

void someip_client::stop()
{
    // unregister the state handler
    app_->unregister_state_handler();
    // unregister the message handler
    app_->unregister_message_handler(vsomeip::ANY_SERVICE,
            service_instance_id, vsomeip::ANY_METHOD);
    // alternatively unregister all registered handlers at once
    app_->clear_all_handler();
    // release the service
    app_->release_service(service_id, service_instance_id);
    // shutdown the application
    app_->stop();
}

void someip_client::on_state_cbk(vsomeip::state_type_e _state)
{
    if(_state == vsomeip::state_type_e::ST_REGISTERED)
    {
        // we are registered at the runtime now we can request the service
        // and wait for the on_availability callback to be called
        app_->request_service(service_id, service_instance_id);
    }
}

void someip_client::on_availability_cbk(vsomeip::service_t _service,
        vsomeip::instance_t _instance, bool _is_available)
{
    // Check if the available service is the the hello world service
    if(service_id == _service && service_instance_id == _instance
            && _is_available)
    {
        // The service is available then we send the request
        // Create a new request
        std::shared_ptr<vsomeip::message> rq = rtm_->create_request();
        // Set the hello world service as target of the request
        rq->set_service(service_id);
        rq->set_instance(service_instance_id);
        rq->set_method(service_method_id);

        // Create a payload which will be sent to the service
        std::shared_ptr<vsomeip::payload> pl = rtm_->create_payload();
        std::string str("World");
        std::vector<vsomeip::byte_t> pl_data(std::begin(str), std::end(str));

        pl->set_data(pl_data);
        rq->set_payload(pl);
        // Send the request to the service. Response will be delivered to the
        // registered message handler
        std::cout << "Sending: " << str << std::endl;
        app_->send(rq, true);
    }
}

void someip_client::on_message_cbk(const std::shared_ptr<vsomeip::message> &_response)
{
    if(service_id == _response->get_service()
            && service_instance_id == _response->get_instance()
            && vsomeip::message_type_e::MT_RESPONSE
                    == _response->get_message_type()
            && vsomeip::return_code_e::E_OK == _response->get_return_code())
    {
        // Get the payload and print it
        std::shared_ptr<vsomeip::payload> pl = _response->get_payload();
        std::string resp = std::string(
                reinterpret_cast<const char*>(pl->get_data()), 0,
                pl->get_length());
        std::cout << "Received: " << resp << std::endl;
        stop();
    }
}

} // namespace someip_app
